﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using Common;
using Common.Entities;
using Common.Enums;
using Common.Extensions;
using Common.Packets;
using GameServer.Entities;
using Lidgren.Network;

namespace GameServer
{
    public partial class Main : Form
    {
        private Process proc;
        private PerformanceCounter cpuCounter;
        private long maxMem = 0;
        private long minMem = long.MaxValue;
        private float maxCPU = 0;
        private float minCPU = float.MaxValue;
        public static NetServer Server;
        private static Stopwatch StopWatch = new Stopwatch();
        private bool _isExiting = false;
        Random rnd = new Random();
        private int port;
        public NetClient MasterServerClient;
        private NetPeerConfiguration config;// = new NetPeerConfiguration("ClickGame");
        public Dictionary<string, string> PlayersTokens = new Dictionary<string, string>();
        //List<NetConnection> _clients = new List<NetConnection>();
        List<User> _clients = new List<User>();
        public decimal _inputQueries = 0.0m;//запросы на запись в базу
        public decimal _outputQueries = 0.0m;//запросы на чтение из бд
        public Main()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer, true);
            DoubleBuffered = true;
            StopWatch.Start();
            proc = Process.GetCurrentProcess();
            cpuCounter = new PerformanceCounter("Process", "% Processor Time", proc.ProcessName, true);
            while (true)
            {
                port = 15324;
                try
                {
                    config = new NetPeerConfiguration("ClickGame");
                    config.Port = port;
                    //config.SimulatedLoss = 0.02f;
                    config.MaximumConnections = 25600;
                    //config.SimulatedMinimumLatency = 10;
                    //config.ConnectionTimeout = 10;
                    //config.SetMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval, true);
                    Server = new NetServer(config);
                    Server.RegisterReceivedCallback(new SendOrPostCallback(ServerLoop));
                    Server.Start();
                    new Thread(MainLoop).Start();
                    break;
                }
                catch
                { }
            }
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;  // Turn on WS_EX_COMPOSITED
                return cp;
            }
        }

        public void AddConsoleLog(string text, ConsoleMessageType messageType)
        {
            if (!_isExiting)
            {
                string log = string.Format("{0} {1} {2}", DateTime.Now,
                                           messageType.GetDescription(), text);
                try
                {
                    this.Invoke(
                        new Action(
                            () =>
                            {
                                NativeMethods.AppendText(logTb,
                                                         log);
                            }));
                }
                catch
                { }
                using (FileStream fs = new FileStream("Log.log", FileMode.Append, FileAccess.Write))
                {
                    using (StreamWriter sw = new StreamWriter(fs))
                    {
                        sw.WriteLine(log);
                        sw.Close();
                    }
                    fs.Close();
                }
            }
        }

        public void ServerLoop(object peer)
        {
            double nextSendUpdates = NetTime.Now;
            NetIncomingMessage msg;
            while ((msg = Server.ReadMessage()) != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.VerboseDebugMessage:
                    case NetIncomingMessageType.DebugMessage:
                        AddConsoleLog(msg.ReadString(), ConsoleMessageType.Info);
                        break;
                    case NetIncomingMessageType.WarningMessage:
                        AddConsoleLog(msg.ReadString(), ConsoleMessageType.Warning);
                        break;
                    case NetIncomingMessageType.ErrorMessage:
                        AddConsoleLog(msg.ReadString(), ConsoleMessageType.Error);
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
                        if (status == NetConnectionStatus.Connected)
                        {
                            AddConsoleLog(string.Format("New connection! {0}", msg.SenderConnection.RemoteEndPoint), ConsoleMessageType.Info);
                        }
                        if (status == NetConnectionStatus.Disconnected)
                        {
                            User u = (User)msg.SenderConnection.Tag;
                            if (u != null)
                            {
                                lock (_clients)
                                {
                                    new Thread(() => { Save(u); }).Start();
                                    //=======говнокод======
                                    int id = -1;
                                    for (int i = 0; i < connectedLv.Items.Count; i++)
                                    {
                                        if (connectedLv.Items[i].Text == u.Name)
                                        {
                                            id = i;
                                            break;
                                        }
                                    }
                                    if (id >= 0)
                                    {
                                        this.Invoke(new Action(() => { connectedLv.Items.RemoveAt(id); }));
                                    }
                                    //=======говнокод======
                                    _clients.Remove(u);
                                }
                            }
                            AddConsoleLog(string.Format("Client disconnected! {0} {1}", msg.ReadString(), msg.SenderConnection.RemoteEndPoint), ConsoleMessageType.Info);
                        }
                        if (status == NetConnectionStatus.Disconnecting)
                        {

                        }
                        break;
                    case NetIncomingMessageType.ConnectionApproval:
                        break;
                    case NetIncomingMessageType.Data:
                        PacketType type = (PacketType)msg.ReadInt16();
                        ProcessPacket(msg, type);
                        break;
                }
            }
        }

        public void Save(User u)
        {
            DB db = new DB();
            accounts acc = db.accounts.FirstOrDefault(p => p.login == u.Name);
            _outputQueries++;
            if (acc == null)
                return;
            long id = acc.id;
            //saving player data
            players player = db.players.FirstOrDefault(p => p.acc_id == id);
            _outputQueries++;
            if (player == null)
            {
                player = new players();
                player.advancedpryanchel = u.AdvancedGingerMan;
                player.exp = u.Exp;
                player.glasur = u.Glasur;
                player.pryanchel = u.GingerMan;
                player.pryanik = u.Money;
                player.lvl = u.Lvl;
                player.acc_id = id;
                player.maxmoney = u.MaxMoney;
                player.totalginger = u.TotalGainedGingers;
                db.players.AddObject(player);
            }
            else
            {
                player.advancedpryanchel = u.AdvancedGingerMan;
                player.exp = u.Exp;
                player.glasur = u.Glasur;
                player.pryanchel = u.GingerMan;
                player.pryanik = u.Money;
                player.lvl = u.Lvl;
                player.maxmoney = u.MaxMoney;
                player.totalginger = u.TotalGainedGingers;
            }
            //saving structure data
            List<buildings> structures = db.buildings.Where(p => p.userid == acc.id).ToList();
            _outputQueries++;
            foreach (KeyValuePair<int, long> structure in u.Structures)
            {
                buildings building = structures.FirstOrDefault(p => p.building == structure.Key);
                bool flag = false;
                if (building == null)
                {
                    building = new buildings();
                    flag = true;
                }
                building.userid = acc.id;
                building.building = structure.Key;
                building.buildinglvl = (int)structure.Value;
                if (flag)
                {
                    db.buildings.AddObject(building);
                }
            }
            List<researches> upgrades = db.researches.Where(p => p.userid == acc.id).ToList();
            _outputQueries++;
            foreach (int up in u.Upgrades)
            {
                if (upgrades.FirstOrDefault(p => p.researchid == up) == null)
                {
                    researches r = new researches();
                    r.researchid = up;
                    r.userid = acc.id;
                    db.researches.AddObject(r);
                }
            }

            _inputQueries += db.SaveChanges();
        }

        public void ProcessPacket(NetIncomingMessage msg, PacketType type)
        {
            User u;
            switch (type)
            {
                case PacketType.Login:
                    string login = msg.ReadString();
                    string pass = msg.ReadString();
                    new Thread(() =>
                    {
                        Login(login, pass, msg.SenderConnection);
                    }).Start();
                    break;
                case PacketType.Enter:
                    break;
                case PacketType.Refresh:
                    u = (User)msg.SenderConnection.Tag;
                    TimeSpan ts = DateTime.Now - u.LastRefresh;
                    byte gingerclicks = msg.ReadByte();
                    byte glasurclicks = msg.ReadByte();
                    byte gingermanclicks = msg.ReadByte();
                    byte advgingermanclicks = msg.ReadByte();
                    if (gingerclicks / ts.TotalSeconds > 8)
                    {
                        //TODO: читер, вся фигня
                        break;
                    }
                    if (glasurclicks / ts.TotalSeconds > 8)
                    {
                        //TODO: читер, вся фигня
                        break;
                    }
                    if (gingermanclicks / ts.TotalSeconds > 8)
                    {
                        //TODO: читер, вся фигня
                        break;
                    }
                    if (advgingermanclicks / ts.TotalSeconds > 8)
                    {
                        //TODO: читер, вся фигня
                        break;
                    }
                    u.Money += u.ClickIncome * gingerclicks;
                    if (u.MaxMoney < u.Money)
                    {
                        u.MaxMoney = u.Money;
                    }
                    u.TotalGainedGingers += u.ClickIncome * gingerclicks;
                    u.Glasur += u.GlasurClickIncome * glasurclicks;
                    if (u.Money >= 1000.0m * gingerclicks)
                    {
                        u.GingerMan += gingermanclicks;
                        u.Money -= gingermanclicks * 1000.0m;
                    }
                    if (u.Money >= 10000.0m * advgingermanclicks && u.GingerMan >= 3 * advgingermanclicks)
                    {
                        u.AdvancedGingerMan += advgingermanclicks;
                        u.GingerMan -= advgingermanclicks;
                        u.Money -= advgingermanclicks * 10000.0m;
                    }
                    u.LastRefresh = DateTime.Now;
                    IFormatter formatter = new BinaryFormatter();
                    MemoryStream stream = new MemoryStream();
                    formatter.Serialize(stream, u);
                    stream.Position = 0;
                    stream.Close();
                    byte[] data = stream.ToArray();
                    NetOutgoingMessage nom = Server.CreateMessage();
                    nom.Write((short)PacketType.Refresh);
                    nom.Write(data.Length);
                    nom.Write(data);
                    msg.SenderConnection.SendMessage(nom, NetDeliveryMethod.ReliableOrdered, 0);
                    break;
                case PacketType.BuyStructure:
                    u = (User)msg.SenderConnection.Tag;
                    int id = msg.ReadInt32();
                    long count = GetUserStructureCount(u, id);
                    decimal price = CountStructurePrice(id, count);
                    if (u.Money < price)
                    {
                        break;
                    }
                    if (u.Structures == null)
                    {
                        u.Structures = new Dictionary<int, long>();
                    }
                    if (!u.Structures.ContainsKey(id))
                    {
                        u.Structures.Add(id, 1);
                    }
                    else
                    {
                        u.Structures[id] = count + 1;
                    }
                    u.Money -= price;
                    break;
                case PacketType.Chat:
                    u = (User)msg.SenderConnection.Tag;
                    byte channel = msg.ReadByte();
                    string message = msg.ReadString();
                    AddConsoleLog(string.Format("[Chat]({0}){1}:{2}",channel,u.Name,message),ConsoleMessageType.Info);
                    string nick = "";
                    if (message.StartsWith("/pm") || channel == 1)
                    {
                        if ((channel == 1 && message.Split(' ').Length < 2) || (message.StartsWith("/pm") && message.Split(' ').Length < 3))
                        {
                            NetOutgoingMessage om = Server.CreateMessage();
                            //om = Server.CreateMessage();
                            om.Write((short)PacketType.Chat);
                            om.Write((byte)2);
                            om.Write("Server: не верный формат команды!");
                            Server.SendMessage(om, msg.SenderConnection, NetDeliveryMethod.ReliableOrdered);
                            return;
                        }
                        nick = message.Split(' ')[1];
                        if (channel == 1)
                        {
                            nick = message.Split(' ')[0];
                        }
                        channel = 1;
                    }
                    //TODO: сделать обработку команд чата
                    if (channel == 0)//global message
                    {
                        NetOutgoingMessage om = Server.CreateMessage();
                        om.Write((short)PacketType.Chat);
                        om.Write(channel);
                        om.Write(u.Name + ": " + message);
                        //foreach(NetConnection client in _clients)
                        //{
                        Server.SendToAll(om, NetDeliveryMethod.ReliableOrdered);
                        //client.SendMessage(om, NetDeliveryMethod.ReliableOrdered, 0);
                        //}

                    }
                    if (channel == 1)//private message
                    {
                        User user = _clients.FirstOrDefault(p => p.Name.ToLower() == nick.ToLower());


                        NetConnection sender = msg.SenderConnection;
                        NetOutgoingMessage om;
                        if (user == null)
                        {
                            //TODO: show warning, no player
                            om = Server.CreateMessage();
                            om.Write((short)PacketType.Chat);
                            om.Write((byte)2);
                            om.Write("Server: Игрока " + nick + " нет в сети!");
                            Server.SendMessage(om, sender, NetDeliveryMethod.ReliableOrdered);
                            return;
                        }
                        NetConnection recipient = user.Connection;
                        //User user = recipient.Tag as User;
                        if (u.Name == nick)
                        {
                            om = Server.CreateMessage();
                            om.Write((short)PacketType.Chat);
                            om.Write((byte)2);
                            om.Write("Server: нельзя отправить личное сообщение самому себе!");
                            Server.SendMessage(om, sender, NetDeliveryMethod.ReliableOrdered);
                            return;
                        }

                        om = Server.CreateMessage();
                        message = message.Replace("/pm " + nick + " ", "");
                        om.Write((short)PacketType.Chat);
                        om.Write(channel);
                        om.Write("[To]" + user.Name + ": " + message);
                        Server.SendMessage(om, sender, NetDeliveryMethod.ReliableOrdered);
                        om = Server.CreateMessage();
                        om.Write((short)PacketType.Chat);
                        om.Write(channel);
                        om.Write("[From]" + u.Name + ": " + message);
                        Server.SendMessage(om, recipient, NetDeliveryMethod.ReliableOrdered);
                    }
                    break;
                case PacketType.GoldenGinger:
                    u = (User)msg.SenderConnection.Tag;
                    if (u.LastGoldenGingerAppeared.AddSeconds(20) < DateTime.Now) //fucked up golden ginger
                    {
                        break;
                    }
                    u.LastGoldenGingerClicked = DateTime.Now;
                    u.Multiplier += 6.0f;
                    //TODO: check all multipliers
                    break;
                case PacketType.Upgrade:
                    u = (User)msg.SenderConnection.Tag;
                    id = msg.ReadInt32();
                    Upgrade up = Upgrades.List[id];
                    bool allConditions = true;

                    if (u.Upgrades.Contains(up.Id))//уже купили апгрейд
                    {
                        allConditions = false;
                    }
                    if (up.AchievementsNeed != null)
                    {
                        foreach (int uid in up.AchievementsNeed)
                        {
                            if (!u.Achievements.Contains(uid))
                            {
                                allConditions = false;
                                break;
                            }
                        }
                    }
                    if (up.StructuresNeed != null)
                    {
                        foreach (KeyValuePair<int, int> s in up.StructuresNeed)
                        {
                            if (!u.Structures.ContainsKey(s.Key))
                            {
                                allConditions = false;
                                break;
                            }
                            else
                            {
                                if (u.Structures[s.Key] < s.Value)
                                {
                                    allConditions = false;
                                    break;
                                }
                            }
                        }
                    }
                    if (u.Money < up.Cost)
                    {
                        allConditions = false;
                    }
                    if (allConditions)
                    {
                        u.Upgrades.Add(id);
                        u.Money -= up.Cost;
                        //TODO:add bonuses
                    }
                    break;
                default:
                    break;
            }
        }

        long GetUserStructureCount(User u, int id)
        {
            long r = 0;
            if (u.Structures != null)
            {
                if (u.Structures.ContainsKey(id))
                {
                    r = u.Structures[id];
                }
            }
            return r;
        }

        private decimal CountStructurePrice(int id, double count)
        {
            decimal r = Structures.List[id].BasePrice;
            r = r * (decimal)Math.Pow(1.15d, count);
            return r;
        }

        public void Login(string login, string pass, NetConnection connection)
        {
            bool loginok = true;
            Parallel.For(0, _clients.Count, (i) =>
            {
                if (_clients[i].Name == login)
                {
                    connection.Disconnect("Игрок с этим ником уже в сети!");
                    loginok = false;
                }
            });
            if (!loginok)
                return;

            DB db = new DB();
            accounts acc = db.accounts.FirstOrDefault(p => p.login == login && p.password == pass);
            _outputQueries++;

            NetOutgoingMessage nom = Server.CreateMessage();
            if (acc == null)
            {
                connection.Disconnect("Неверный логин или пароль!");
                //nom.Write((short)PacketType.Login);
                //nom.Write(false);
                //nom.Write("Неверный логин или пароль!");
                //connection.SendMessage(nom, NetDeliveryMethod.ReliableOrdered, 0);
                return;
            }

            nom.Write((short)PacketType.Login);
            nom.Write(true);
            List<bans> bans = db.bans.Where(p => p.acc_id == acc.id && p.banned_until >= DateTime.Now).ToList();
            _outputQueries++;
            if (bans.Count > 0)
            {
                long banner_id = bans[0].who_banned;
                accounts banner = db.accounts.FirstOrDefault(p => p.id == banner_id);
                _outputQueries++;
                string banner_name = banner != null ? banner.login : "администратором";
                connection.Disconnect(string.Format("Вы были забанены {0} по причине: \"{1}\" до {2}", banner_name, bans[0].ban_reason, bans[0].banned_time));
                return;
            }
            players player = db.players.FirstOrDefault(p => p.acc_id == acc.id);
            _outputQueries++;
            User user = new User();
            if (player == null)
            {
                user.Money = 0;
                user.Glasur = 0;
                user.GingerMan = 0;
                user.AdvancedGingerMan = 0;
                user.Lvl = 0;
                user.Name = acc.login;
                user.Exp = 0;
                user.LastRefresh = DateTime.Now;
                user.Structures = new Dictionary<int, long>();
                user.Achievements = new List<int>();
                user.Upgrades = new List<int>();
                Save(user);
            }
            else
            {
                user.Money = player.pryanik;
                user.Glasur = player.glasur;
                user.GingerMan = player.pryanchel;
                user.AdvancedGingerMan = player.advancedpryanchel;
                user.Lvl = (int)player.lvl;
                user.Name = acc.login;
                user.Exp = (int)player.exp;
                user.LastRefresh = DateTime.Now;
                user.MaxMoney = player.maxmoney;
                user.TotalGainedGingers = player.totalginger;
                //user.Income = 100.01m;
                //user.ClickIncome = 100.01m;
            }


            List<buildings> structures = db.buildings.Where(p => p.userid == acc.id).ToList();
            _outputQueries++;
            user.Structures = new Dictionary<int, long>();
            foreach (buildings structure in structures)
            {
                user.Structures.Add(structure.building, structure.buildinglvl);
                decimal gingerProfit = Structures.List[structure.building].Income * structure.buildinglvl;//
                decimal glasurProfit = Structures.List[structure.building].GlasurIncome * structure.buildinglvl;
                decimal gingermanProfit = Structures.List[structure.building].GingerManIncome * structure.buildinglvl;
                decimal advgingermanProfit = Structures.List[structure.building].AdvancedGingerManIncome * structure.buildinglvl;
                if (gingerProfit < 0 || glasurProfit < 0 || gingermanProfit < 0 || advgingermanProfit < 0)//если не хватает ресурса для заводов - они не работают и не дают прибыли
                {
                    if (user.Money < -gingerProfit || user.Glasur < -glasurProfit ||
                        user.GingerMan < -gingermanProfit || user.AdvancedGingerMan < -advgingermanProfit)
                    {
                        gingerProfit = 0;
                        glasurProfit = 0;
                        gingermanProfit = 0;
                        advgingermanProfit = 0;
                    }
                }
                user.Income += gingerProfit;
                user.GlasurIncome += glasurProfit;
                user.GingerManIncome += gingermanProfit;
                user.AdvancedGingerManIncome += advgingermanProfit;
            }
            //TODO: добавить влияние апгрейдов
            user.ClickIncome = user.Income / 1000.0m;
            if (user.ClickIncome < 1)
                user.ClickIncome = 1;
            user.GlasurClickIncome = user.GlasurIncome / 1000.0m;

            user.Upgrades = new List<int>();
            List<researches> upgrades = db.researches.Where(p => p.userid == acc.id).ToList();
            _outputQueries++;
            foreach (researches upgrade in upgrades)
            {
                user.Upgrades.Add((int)upgrade.researchid);
            }

            user.Achievements = new List<int>();
            //TODO:сделать загрузку апгрейдов и т.п.

            IFormatter formatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            formatter.Serialize(stream, user);
            stream.Position = 0;
            stream.Close();
            byte[] data = stream.ToArray();

            nom.Write(data.Length);
            nom.Write(data);
            connection.Tag = user;
            user.Connection = connection;
            _clients.Add(user);
            //TODO:nom.Write(accInfo);
            connection.SendMessage(nom, NetDeliveryMethod.ReliableOrdered, 0);
            ListViewItem item = new ListViewItem(user.Name);
            item.Tag = connection;
            this.Invoke(new Action(() => { connectedLv.Items.Add(item); }));
        }

        public void MainLoop()
        {
            Stopwatch sw = new Stopwatch();
            int seconds = 0;
            while (!_isExiting)
            {
                sw.Start();
                lock (_clients)
                {
                    Parallel.For(0, _clients.Count, (i) =>
                    {
                        User u = _clients[i];
                        u.Income = 0;
                        u.GlasurIncome = 0;
                        u.GingerManIncome = 0;
                        u.AdvancedGingerManIncome = 0;
                        float structBonus = 0.0f;
                        u.Multiplier = 0.0f;
                        if (u.LastGoldenGingerClicked.AddSeconds(60) < DateTime.Now)
                        {
                            u.Multiplier = 0.0f;
                        }
                        else
                        {
                            u.Multiplier = 6.0f;
                        }
                        foreach (KeyValuePair<int, long> structure in u.Structures)
                        {
                            foreach (int up in u.Upgrades)
                            {
                                if (Upgrades.List[up].Structures != null)
                                {
                                    if (Upgrades.List[up].Structures.ContainsKey(structure.Key))
                                    {
                                        structBonus = Upgrades.List[up].Structures[structure.Key];
                                    }
                                }
                            }
                            decimal gingerProfit = (Structures.List[structure.Key].Income * structure.Value) * (1.0m + (decimal)structBonus);//
                            decimal glasurProfit = (Structures.List[structure.Key].GlasurIncome * structure.Value) * (1.0m + (decimal)structBonus);
                            decimal gingermanProfit = (Structures.List[structure.Key].GingerManIncome * structure.Value) * (1.0m + (decimal)structBonus);
                            decimal advgingermanProfit = (Structures.List[structure.Key].AdvancedGingerManIncome * structure.Value) * (1.0m + (decimal)structBonus);
                            if (gingerProfit < 0 || glasurProfit < 0 || gingermanProfit < 0 || advgingermanProfit < 0)//если не хватает ресурса для заводов - они не работают и не дают прибыли
                            {
                                if (u.Money < -gingerProfit || u.Glasur < -glasurProfit ||
                                    u.GingerMan < -gingermanProfit || u.AdvancedGingerMan < -advgingermanProfit)
                                {
                                    gingerProfit = 0;
                                    glasurProfit = 0;
                                    gingermanProfit = 0;
                                    advgingermanProfit = 0;
                                }
                            }
                            u.Income += gingerProfit;
                            u.GlasurIncome += glasurProfit;
                            u.GingerManIncome += gingermanProfit;
                            u.AdvancedGingerManIncome += advgingermanProfit;
                        }
                        //TODO: добавить влияние апгрейдов
                        foreach (int up in u.Upgrades)
                        {
                            u.Multiplier += Upgrades.List[up].BonusToIncome;
                        }
                        if (u.Multiplier > 0)
                        {
                            u.Money += u.Income * (decimal)u.Multiplier;
                        }
                        u.Money += u.Income;
                        u.TotalGainedGingers += u.Income + (u.Income * (decimal)u.Multiplier);
                        if (u.Money > u.MaxMoney)
                        {
                            u.MaxMoney = u.Money;
                        }
                        u.ClickIncome = (u.Multiplier > 0) ? (u.Income / 1000.0m) : ((u.Income + (u.Income * (decimal)u.Multiplier)) / 1000.0m);
                        if (u.ClickIncome < 1)
                            u.ClickIncome = 1;
                        u.GingerMan += u.GingerManIncome;
                        u.GlasurClickIncome = u.GlasurIncome / 1000.0m;
                        u.Glasur += u.GlasurIncome;
                        u.AdvancedGingerMan += u.AdvancedGingerManIncome;
                        //u.LastRefresh = DateTime.Now;
                        if (u.LastGoldenGingerAppeared.AddSeconds(20) < DateTime.Now)//frequently appear golden ginger, but not oftener one in 10 minutes
                        {
                            if (rnd.Next(10) == 6)
                            {
                                NetOutgoingMessage om = Server.CreateMessage();
                                om.Write((short)PacketType.GoldenGinger);
                                Server.SendMessage(om, u.Connection, NetDeliveryMethod.ReliableOrdered);
                                u.LastGoldenGingerAppeared = DateTime.Now;
                            }
                        }
                        
                        foreach (KeyValuePair<int, Achievement> achievement in Achievements.List)
                        {
                            if (!u.Achievements.Contains(achievement.Key))
                            {
                                Achievement a = achievement.Value;
                                bool allConditions = true;
                                if (u.TotalGainedGingers < a.Money || u.Income < a.MoneyIncome || u.GingerMan < a.GingerMan ||
                                    u.GingerManIncome < a.GingerManIncome || u.Glasur < a.Glasur ||
                                    u.GlasurIncome < a.GlasurIncome || u.AdvancedGingerMan < a.AdvancedGingerMan ||
                                    u.AdvancedGingerManIncome < a.AdvancedGingerManIncome)
                                {
                                    allConditions = false;
                                }
                                if (a.Structures != null)
                                {
                                    foreach (KeyValuePair<int, int> s in a.Structures)
                                    {
                                        if (u.Structures.ContainsKey(s.Key))
                                        {
                                            if (u.Structures[s.Key] < s.Value)
                                            {
                                                allConditions = false;
                                            }
                                        }
                                        else
                                        {
                                            allConditions = false;
                                        }
                                    }
                                }
                                if (a.Upgrades != null)
                                {
                                    foreach (int up in a.Upgrades)
                                    {
                                        if (!u.Upgrades.Contains(up))
                                        {
                                            allConditions = false;
                                        }
                                    }
                                }
                                if (allConditions)
                                    u.Achievements.Add(a.Id);
                            }
                        }
                    });
                }
                seconds++;
                if (seconds >= 600)//сохранение раз в 10 минут
                {
                    new Thread(() =>
                    {
                        Parallel.For(0, _clients.Count, (i) =>
                        {
                            Save(_clients[i]);
                        });
                    }).Start();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();
                    seconds = 0;
                }
                sw.Stop();
                int t = 1000 - (int)sw.ElapsedMilliseconds;
                sw.Reset();
                if (t > 0)
                {
                    Thread.Sleep(t);
                }

            }
        }

        public List<long> MemoryValues = new List<long>();
        public List<float> CPUValues = new List<float>();

        private void countersTimer_Tick(object sender, EventArgs e)
        {

            float cpuValue = cpuCounter.NextValue() / Environment.ProcessorCount;
            CPUValues.Add(cpuValue);
            proc = Process.GetCurrentProcess();

            if (minMem > proc.PrivateMemorySize64)
            {
                minMem = proc.PrivateMemorySize64;
            }
            if (maxMem < proc.PrivateMemorySize64)
            {
                maxMem = proc.PrivateMemorySize64;
            }
            if (minCPU > cpuValue)
            {
                minCPU = cpuValue;
            }
            if (maxCPU < cpuValue)
            {
                maxCPU = cpuValue;
            }
            MemoryValues.Add(proc.PrivateMemorySize64);
            if (MemoryValues.Count > 300)
            {
                MemoryValues.RemoveAt(0);
            }
            if (CPUValues.Count > 300)
            {
                CPUValues.RemoveAt(0);
            }

            if (tabControl1.SelectedIndex != 1)
                return;
            cpuMaxLbl.Text = "CPU максимум: " + maxCPU.ToString("N0") + "%";
            cpuMinLbl.Text = "CPU минимум: " + minCPU.ToString("N0") + "%";
            cpuAvLbl.Text = "CPU в среднем за 5 минут: " + CPUValues.Average().ToString("N0") + "%";
            memMaxLbl.Text = "Максимум памяти: " + GetMemoryValue(maxMem);
            memMinLbl.Text = "Минимум памяти: " + GetMemoryValue(minMem);
            memAvLbl.Text = "Памяти в среднем за 5 минут: " + GetMemoryValue((long)MemoryValues.Average());
            cpuLbl.Text = "CPU: " + cpuValue.ToString("N0") + "%";
            memLbl.Text = "Памяти занято: " + GetMemoryValue(proc.PrivateMemorySize64);
            incTrafLbl.Text = "Входящий траффик: " + GetMemoryValue(Server.Statistics.ReceivedBytes);
            outTrafLbl.Text = "Исходящий траффик: " + GetMemoryValue(Server.Statistics.SentBytes);
            totalTrafLbl.Text = "Всего: " + GetMemoryValue(Server.Statistics.SentBytes + Server.Statistics.ReceivedBytes);
            inMessagesLbl.Text = "Входящих сообщений: " + Server.Statistics.ReceivedMessages;
            outMessagesLbl.Text = "Исходящих сообщений: " + Server.Statistics.SentMessages;
            totalMessagesLbl.Text = "Всего: " + (Server.Statistics.ReceivedMessages + Server.Statistics.SentMessages);
            inPacketsLbl.Text = "Входящих пакетов: " + Server.Statistics.ReceivedPackets;
            outPacketsLbl.Text = "Исходящих пакетов: " + Server.Statistics.SentPackets;
            totalPacketsLbl.Text = "Всего: " + (Server.Statistics.ReceivedPackets + Server.Statistics.SentPackets);
            connectionsLbl.Text = "Соединений: " + Server.ConnectionsCount;
            inputMysqlLbl.Text = "Записано запросов в базу:" + _inputQueries;
            outputMysqlLbl.Text = "Считано запросов из базы:" + _outputQueries;
            overallMysqlLbl.Text = "Всего запросов к базе:" + (_inputQueries + _outputQueries);

            TimeSpan TimeDiff = StopWatch.Elapsed;

            uptimeLbl.Text = string.Format("Время работы: {0:00}:{1:00}:{2:00}:{3:00}",
               TimeDiff.Days,
               TimeDiff.Hours,
               TimeDiff.Minutes,
               TimeDiff.Seconds);
            graphicPanel.Invalidate();
        }

        private void graphicPanel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            float rectWidth = (float)graphicPanel.Width / 300.0f;
            float maxValueCPU = CPUValues.Max();
            long maxValueMem = MemoryValues.Max();
            for (int i = 0; i < MemoryValues.Count; i++)
            {
                g.FillRectangle(new SolidBrush(Color.Black), graphicPanel.Width - (float)(MemoryValues.Count - i) * rectWidth, graphicPanel.Height - ((float)MemoryValues[i] / (float)maxValueMem * (float)graphicPanel.Height), rectWidth, graphicPanel.Height);
                g.FillRectangle(new SolidBrush(Color.Red), graphicPanel.Width - (float)(MemoryValues.Count - i) * rectWidth, graphicPanel.Height - ((float)CPUValues[i] / (float)maxValueCPU * (float)graphicPanel.Height), rectWidth, graphicPanel.Height);
            }
            g.FillRectangle(new SolidBrush(Color.LightGray), graphicPanel.Width - 30, graphicPanel.Height - 15, 20, 15);
            g.FillRectangle(new SolidBrush(Color.LightGray), graphicPanel.Width - 106, 0, 94, 15);
            g.DrawString(maxValueCPU.ToString("N0") + "% / " + GetMemoryValue(maxValueMem), this.Font, new SolidBrush(Color.Black), graphicPanel.Width - 100, 0);
            g.DrawString("0", this.Font, new SolidBrush(Color.Black), graphicPanel.Width - 25, graphicPanel.Height - 15);
            g.Save();
        }
        public string GetMemoryValue(long value)
        {
            double temp;
            if (value < 1024)
            {
                return value.ToString();
            }
            if (value < 1024 * 1024)
            {
                temp = value / 1024.0d;
                return temp.ToString("N2") + "KB";
            }
            if (value < 1024 * 1024 * 1024)
            {
                temp = value / 1024.0d / 1024.0d;
                return temp.ToString("N2") + "MB";
            }
            else
            {
                temp = value / 1024.0d / 1024.0d / 1024.0d;
                return temp.ToString("N2") + "GB";
            }
        }

        private void Main_FormClosed(object sender, FormClosedEventArgs e)
        {
            AddConsoleLog("Server shutdown by user.", ConsoleMessageType.Info);
            _isExiting = true;
        }

        private void connectedLv_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                ListViewItem item = connectedLv.GetItemAt(e.X, e.Y);
                Point p = connectedLv.PointToScreen(e.Location);
                if (item != null)
                {
                    playerAdminCM.Tag = item.Text;
                    playerAdminCM.Show(p);
                }
            }
        }

        private void banToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string name = playerAdminCM.Tag as string;
            BanKickWindow bkw = new BanKickWindow(0, name);
            if (bkw.ShowDialog() == DialogResult.OK)
            {
                foreach (User user in _clients)
                {
                    //User user = client.Tag as User;
                    if (user.Name == name)
                    {
                        DB db = new DB();
                        Save(user);
                        user.Connection.Disconnect(string.Format("Вы были забанены администратором по причине: \"{0}\" до {1}", bkw.Reason, bkw.Date));
                        accounts acc = db.accounts.FirstOrDefault(p => p.login == name);
                        _outputQueries++;
                        if (acc != null)
                        {
                            bans ban = new bans();
                            ban.acc_id = acc.id;
                            ban.ban_reason = bkw.Reason;
                            ban.banned_time = DateTime.Now;
                            ban.banned_until = bkw.Date;
                            ban.who_banned = 0;
                            db.bans.AddObject(ban);
                            db.SaveChanges();
                            _inputQueries++;
                        }
                    }
                }
            }
        }

        private void kickToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string name = playerAdminCM.Tag as string;
            BanKickWindow bkw = new BanKickWindow(1, name);
            if (bkw.ShowDialog() == DialogResult.OK)
            {
                foreach (User user in _clients)
                {
                    //User user = client.Tag as User;
                    if (user.Name == name)
                    {
                        Save(user);
                        user.Connection.Disconnect(string.Format("Вас кикнул администратор по причине: {0}", bkw.Reason));

                    }
                }
            }
        }

        private void viewInfoToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void sendMessageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string name = playerAdminCM.Tag as string;
            BanKickWindow bkw = new BanKickWindow(2, name);
            if (bkw.ShowDialog() == DialogResult.OK)
            {
                foreach (User user in _clients)
                {
                    //User user = client.Tag as User;
                    if (user.Name == name)
                    {
                        Save(user);
                        NetOutgoingMessage om = Server.CreateMessage();
                        om.Write((short)PacketType.Message);
                        om.Write(bkw.Reason);
                        user.Connection.SendMessage(om, NetDeliveryMethod.ReliableOrdered, 0);
                        //client.Disconnect(string.Format("Вас кикнул администратор по причине: {0}", bkw.Reason));

                    }
                }
            }
        }

        void SendAdminMsg(string msg)
        {
            NetOutgoingMessage om = Server.CreateMessage();
            om.Write((short)PacketType.Chat);
            om.Write((byte)2);
            om.Write(msg);
            Server.SendToAll(om, NetDeliveryMethod.ReliableOrdered);
            AddConsoleLog(string.Format("[Chat](2):{0}", msg), ConsoleMessageType.Info);
        }

        private void sendBtn_Click(object sender, EventArgs e)
        {
            SendAdminMsg(msgTb.Text);
            msgTb.Clear();
        }

        private void msgTb_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                SendAdminMsg(msgTb.Text);
                msgTb.Clear();
            }
        }
    }
}
